/*
* Licensed under the MIT License
* 
* Copyright (c) 2010 Kay Kreuning
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 
* http://code.google.com/p/as3hyvesapi
*/
package nl.hyves.api.utils
{
	import com.hurlant.crypto.hash.HMAC;
	import com.hurlant.crypto.hash.SHA1;
	import com.hurlant.util.Base64;
	import com.hurlant.util.Hex;
	
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	
	import nl.hyves.api.sessions.OAuthToken;

	public class AuthUtil
	{
		private static const sha1:SHA1 = new SHA1();
		private static const hmac:HMAC = new HMAC(sha1);
		
		/**
		 * Extract an <code>Accesstoken</code> from an OAuth response, it is
		 * this responsibility of the client to make sure that the correct
		 * response (containing an accesstoken) is passed.
		 * 
		 * @param response Object containing the parsed response to an
		 * accesstoken method
		 * @return an <code>Accesstoken</code> instance
		 */
		public static function extractToken(type:String, response:Object):OAuthToken
		{
			return new OAuthToken(
				type,
				response.oauth_token,
				response.oauth_token_secret,
				response.hasOwnProperty("userid") ? response.userid : "",
				response.hasOwnProperty("methods") ? response.methods.split(",") : null,
				response.hasOwnProperty("expiredate") ? new Date(response.expiredate * 1000) : null
			);
		}
		
		/**
		 * Create an OAuth <code>URLRequest</code> from the given parameters.
		 * 
		 * @param parameters Parameters to create the request for
		 * @param url URL to send the request to
		 * @param consumerKey OAuth consumer key used for authentication
		 * @param consumerSecret OAuth consumer secret used to sign the request
		 * @param accesstoken <code>Accesstoken</code> used to sign the request
		 * @return OAuth <code>URLRequest</code>
		 */
		public static function createRequest(parameters:Object, url:String, consumerKey:String, consumerSecret:String, token:OAuthToken = null):URLRequest
		{
			if (token && token.token)
				parameters["oauth_token"] = token.token;
			
			parameters["oauth_nonce"] = int(Math.random() * 0x7FFFFFFF).toString();
			parameters["oauth_timestamp"] = int(new Date().getTime() * 0.001 + 0.5).toString();
			parameters["oauth_consumer_key"] = consumerKey;
			parameters["oauth_signature_method"] = "HMAC-SHA1";
			parameters["oauth_signature"] = calculateHMAC_SHA1Signature(parameters, url, consumerSecret, token ? token.secret : "");
			
			return new URLRequest(url + "?" + decodeParameters(parameters));
		}
		
		/**
		 * Hash a string with the SHA1 hashing algorithm.
		 * 
		 * @param str String to hash
		 * @return sha1( str )
		 */
		public static function hash(str:String):String
		{
			var src:ByteArray = new ByteArray();
			src.writeUTFBytes(str);
			
			var hashed:ByteArray = sha1.hash(src);
			
			return Hex.fromArray(hashed);
		}
		
		/**
		 * Decode an Object into a GET compatible variable string.
		 * 
		 * @param parameters Object to decode
		 * @return GET compatbile variable string
		 */
		private static function decodeParameters(parameters:Object):String
		{
			var array:Array = new Array();
			
			for (var key:String in parameters)
			{
				array.push(key + "=" + encodeURIComponent(parameters[key].toString()));
			}
			
			array.sort();
			
			return array.join("&");
		}
		
		/**
		 * Calculate a HMAC-SHA1 signature the sign a OAuth request.
		 * 
		 * @param parameters Parameters to calculate a signature for
		 * @param url URL to calculate a signature for
		 * @param consumerSecret OAuth consumer secret used to calculate a
		 * signature with
		 * @param tokenSecret Accesstoken secret used to calculate a signature
		 * with
		 * @return HMAC-SHA1 signature
		 */
		private static function calculateHMAC_SHA1Signature(parameters:Object, url:String, consumerSecret:String, tokenSecret:String):String
		{
			var data:String = "GET&" + encodeURIComponent(url) + "&" + encodeURIComponent(decodeParameters(parameters));
			var key:String = encodeURIComponent(consumerSecret) + "&" + encodeURIComponent(tokenSecret || "");
			
			return Base64.encodeByteArray(hmac.compute(Hex.toArray(Hex.fromString(key)), Hex.toArray(Hex.fromString(data))));
		}
	}
}